package com.code.google.mdsc.io.xml.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import com.code.google.mdsc.exception.XMLGeneratorException;
import com.code.google.mdsc.io.log.SimpleLogger;
import com.code.google.mdsc.io.log.impl.SimpleLoggerImpl;
import com.code.google.mdsc.io.xml.XMLSepaGenerator;
import com.code.google.mdsc.io.xml.impl.factory.GrpHdrFactory;
import com.code.google.mdsc.io.xml.impl.factory.PmtInfFactory;
import com.code.google.mdsc.io.xml.impl.threading.XMLSepaGeneratorTask;
import com.code.google.mdsc.model.Booking;
import com.code.google.mdsc.sepa.CustomerDirectDebitInitiationV02;
import com.code.google.mdsc.sepa.Document;
import com.code.google.mdsc.sepa.ObjectFactory;
import com.code.google.mdsc.sepa.PaymentInstructionInformationSDD;

public class XMLSepaGeneratorImpl implements XMLSepaGenerator {

	private static SimpleLogger logger = SimpleLoggerImpl.getInstance();
	private static final String PACKAGE_DIRECTORY_JAR_FILE_XSD_PAIN_008_00X = "com.code.google.msdc.sepa";

	private static final ObjectFactory OBJECT_FACTORY = new ObjectFactory();

	private BigDecimal cashSum = new BigDecimal("0.0");

	private final XMLSepaGeneratorTask xmlsgt;

	public XMLSepaGeneratorImpl(XMLSepaGeneratorTask xmlsgt) {
		this.xmlsgt = xmlsgt;
	}

	public void generate(File dest, Booking bookingData) {

		if (dest == null)
			throw new IllegalArgumentException("destination must not be null");

		Document rootDocument = OBJECT_FACTORY.createDocument();

		CustomerDirectDebitInitiationV02 customerDirectDebitInitiation = OBJECT_FACTORY
				.createCustomerDirectDebitInitiationV02();

		rootDocument.setCstmrDrctDbtInitn(customerDirectDebitInitiation);

		generatePaymentInstructions(customerDirectDebitInitiation.getPmtInf(),
				bookingData);
		customerDirectDebitInitiation.setGrpHdr(new GrpHdrFactory().generate(
				bookingData, cashSum));

		JAXBContext context = null;
		FileOutputStream fos = null;
		try {
			context = JAXBContext
					.newInstance(PACKAGE_DIRECTORY_JAR_FILE_XSD_PAIN_008_00X);
			JAXBElement<Document> element = OBJECT_FACTORY
					.createDocument(rootDocument);
			Marshaller marshaller = context.createMarshaller();
			marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);

			// marshaller.marshal(element, System.out);
			fos = new FileOutputStream(dest);
			marshaller.marshal(element, fos);
			fos.close();
		} catch (JAXBException e) {
			logger.log(SimpleLogger.LogMode.ERROR, e.getMessage());
			throw new XMLGeneratorException(e,
					"could not marshal the xml document");
		} catch (FileNotFoundException e) {
			logger.log(SimpleLogger.LogMode.ERROR, e.getMessage());
			throw new XMLGeneratorException(e, "io for xml document failed");
		} catch (IOException e) {
			logger.log(SimpleLogger.LogMode.ERROR, e.getMessage());
			throw new XMLGeneratorException(e, "io for xml document failed");
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					logger.log(SimpleLogger.LogMode.ERROR, e.getMessage());
					throw new XMLGeneratorException(e,
							"failed to close fos for xml io");
				}
			}
		}

	}

	private void generatePaymentInstructions(
			List<PaymentInstructionInformationSDD> list, Booking paymentData) {
		float progress = 0f;
		int size = paymentData.getDebtors().size();
		for (int i = 0; i < size; i++) {
			logger.log(SimpleLogger.LogMode.DEBUG,
					"generating payment instructions " + (i + 1) + "/" + size);
			cashSum = cashSum.add(paymentData.getDebtors().get(i)
					.getBookingVolume());
			list.add(new PmtInfFactory().generate(paymentData, paymentData
					.getDebtors().size(), cashSum, paymentData.getDebtors()
					.get(i)));

			progress += (100.0 / size);
			xmlsgt.updateProgress((int) progress);
		}
	}
}
